/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#include "pch.h"
#include <baja/transition_tracking/transition_tracking_service.h>
#include <journal/models/page_model.h>
#include <journal/models/page_model_transitions.h>

using namespace journal::model;

namespace bt = baja::transition_tracking;

void page_model::recreatePhoto(
    const std::shared_ptr<bt::generic_transition_parameters>& p)
{
    DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
        boost::wformat(L"page_model::recreatePhoto() : model id = %i : [activate] child photo {id = %i, order = %i}") %
        genericIdentity() %
        p->_objectId %
        p->_objectOrder
        );

    _intermediateRecreatedPhoto = this->recreatePhoto(p->_objectId);
    _intermediateState = intermediate_state::photoRecreated;

    std::shared_ptr<iinternal_photo_model> photo = std::dynamic_pointer_cast<iinternal_photo_model>(_intermediateRecreatedPhoto);
    photo->activate(true);

}

void page_model::redetachPhoto(
    const std::shared_ptr<bt::generic_transition_parameters>& p)
{
    DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
        boost::wformat(L"page_model::redetachPhoto() : model id = %i : [detach] child photo {id = %i, order = %i}") %
        genericIdentity() %
        p->_objectId %
        p->_objectOrder
        );

    b::uint32 photoToRemoveIndex = 0;
    std::shared_ptr<iphoto_model> photoToRemove;

    detachPhotoModel(p->_objectId, photoToRemove, photoToRemoveIndex);

    _intermediateDetachedPhoto = photoToRemove;
    _intermediateState = intermediate_state::photoDetached;
}

void page_model::redeletePhoto(
    const std::shared_ptr<bt::generic_transition_parameters>& p)
{
    DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
        boost::wformat(L"page_model::redeletePhoto() : model id = %i : [deactivate] child photo {id = %i, order = %i}") %
        genericIdentity() %
        p->_objectId %
        p->_objectOrder
        );

    std::shared_ptr<iinternal_photo_model> photo = std::dynamic_pointer_cast<iinternal_photo_model>(_intermediateDetachedPhoto);

    photo->activate(false);

    _intermediateDetachedPhoto = nullptr;   // this should be the last reference to this photo
    _intermediateState = intermediate_state::none;
}

void page_model::reattachPhoto(
    const std::shared_ptr<bt::generic_transition_parameters>& p)
{
    DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
        boost::wformat(L"page_model::reattachPhoto() : model id = %i : [redo attach/undo detach] child photo {id = %i, order = %i}") %
        genericIdentity() %
        p->_objectId %
        p->_objectOrder
        );

    attachPhotoModel(_intermediateRecreatedPhoto);

    _intermediateRecreatedPhoto = nullptr;    // no need to keep hold on to it
    _intermediateState = intermediate_state::none;
}

void page_model::undoSelfAction(
    const std::shared_ptr<bt::itransition_parameters>& transitionParameters)
{
    DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
        boost::wformat(L"page_model::undoSelfAction() : model id = %i") %
        genericIdentity()
        );

    switch (transitionParameters->transitionType())
    {
        case attachChildTransition:
            {
                DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
                    boost::wformat(L"page_model::undoSelfAction() : model id = %i : undo attach child") %
                    genericIdentity()
                    );

                std::shared_ptr<bt::generic_transition_parameters> p = 
                    std::dynamic_pointer_cast<bt::generic_transition_parameters>(transitionParameters);

                if (p->_objectType == _photoModelFactory->typeToken())
                {
                    redetachPhoto(p);
                }
            }
            break;
        case createChildTransition:
            {
                DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
                    boost::wformat(L"page_model::undoSelfAction() : model id = %i : undo create child") %
                    genericIdentity()
                    );

                std::shared_ptr<bt::generic_transition_parameters> p = 
                    std::dynamic_pointer_cast<bt::generic_transition_parameters>(transitionParameters);

                if (p->_objectType == _photoModelFactory->typeToken())
                {
                    redeletePhoto(p);
                }
            }
            break;
        case deleteChildTransition:
            {
                DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
                    boost::wformat(L"page_model::undoSelfAction() : model id = %i : undo delete") %
                    genericIdentity()
                    );

                std::shared_ptr<bt::generic_transition_parameters> p = 
                    std::dynamic_pointer_cast<bt::generic_transition_parameters>(transitionParameters);

                if (p->_objectType == _photoModelFactory->typeToken())
                {
                    recreatePhoto(p);
                }
            }
            break;
        case detachChildTransition:
            DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
                boost::wformat(L"page_model::undoSelfAction() : model id = %i : undo detach") %
                genericIdentity()
                );

                std::shared_ptr<bt::generic_transition_parameters> p = 
                    std::dynamic_pointer_cast<bt::generic_transition_parameters>(transitionParameters);

                if (p->_objectType == _photoModelFactory->typeToken())
                {
                    reattachPhoto(p);
                }

            break;

    }
}

void page_model::redoSelfAction(
    const std::shared_ptr<bt::itransition_parameters>& transitionParameters)
{
    DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
        boost::wformat(L"page_model::redoSelfAction() : model id = %i") %
        genericIdentity()
        );

    switch (transitionParameters->transitionType())
    {
        case createChildTransition:
            {
                DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
                    boost::wformat(L"page_model::redoSelfAction() : model id = %i : redo create child") %
                    genericIdentity()
                    );

                std::shared_ptr<bt::generic_transition_parameters> p = 
                    std::dynamic_pointer_cast<bt::generic_transition_parameters>(transitionParameters);

                if (p->_objectType == _photoModelFactory->typeToken())
                {
                    recreatePhoto(p);
                }
            }
            break;

        case attachChildTransition:
            {
                DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
                    boost::wformat(L"page_model::redoSelfAction() : model id = %i : redo attach child") %
                    genericIdentity()
                    );

                std::shared_ptr<bt::generic_transition_parameters> p = 
                    std::dynamic_pointer_cast<bt::generic_transition_parameters>(transitionParameters);

                if (p->_objectType == _photoModelFactory->typeToken())
                {
                    reattachPhoto(p);
                }
            }
            break;

        case detachChildTransition:
            {
                DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
                    boost::wformat(L"page_model::redoSelfAction() : model id = %i : redo detach") %
                    genericIdentity()
                    );

                std::shared_ptr<bt::generic_transition_parameters> p = 
                    std::dynamic_pointer_cast<bt::generic_transition_parameters>(transitionParameters);

                if (p->_objectType == _photoModelFactory->typeToken())
                {
                    redetachPhoto(p);
                }
            }
            break;

        case deleteChildTransition:
            DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
                boost::wformat(L"page_model::redoSelfAction() : model id = %i : redo delete") %
                genericIdentity()
                );

                std::shared_ptr<bt::generic_transition_parameters> p = 
                    std::dynamic_pointer_cast<bt::generic_transition_parameters>(transitionParameters);

                if (p->_objectType == _photoModelFactory->typeToken())
                {
                    redeletePhoto(p);
                }

            break;
    }
}

void page_model::undoChildAction(
    const std::vector<bt::transition_path_node>& path,
    const b::uint32& childIndex,
    const std::shared_ptr<bt::itransition_parameters>& transitionParameters)
{
    DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
        boost::wformat(L"page_model::undoChildAction() : model id = %i") %
        genericIdentity()
        );

    // check photos...
    if (path[childIndex].typeToken == _photoModelFactory->typeToken())
    {
        b::uint32 photoId = path[childIndex].id;
        std::shared_ptr<bt::itransition_target> photo;

        // find the target photo
        if ( (_intermediateState == intermediate_state::photoDetached) && 
             (_intermediateDetachedPhoto->storageId() == photoId) )
        {
            photo = std::dynamic_pointer_cast<bt::itransition_target>(_intermediateDetachedPhoto);
        }
        if ( (_intermediateState == intermediate_state::photoRecreated) &&
            (_intermediateRecreatedPhoto->storageId() == photoId) )
        {
            photo = std::dynamic_pointer_cast<bt::itransition_target>(_intermediateRecreatedPhoto);
        }
        else
        {
            // it is a page, find the specific page...
            for (auto &iter : _photos)
            {
                if (iter->storageId() == photoId)
                {
                    photo = std::dynamic_pointer_cast<bt::itransition_target>(iter);
                    break;
                }
            }
        }

        // propagate the undo
        if (photo != nullptr)
        {
            photo->undo(path, childIndex, transitionParameters);
        }
    }
}

void page_model::redoChildAction(
    const std::vector<bt::transition_path_node>& path,
    const b::uint32& childIndex,
    const std::shared_ptr<bt::itransition_parameters>& transitionParameters)
{
    DEBUG_CONSOLE_TRACE_TAG(TRACE_TAG_UNDO_REDO,
        boost::wformat(L"page_model::redoChildAction() : model id = %i") %
        genericIdentity()
        );

    // check photos...
    if (path[childIndex].typeToken == _photoModelFactory->typeToken())
    {
        b::uint32 photoId = path[childIndex].id;
        std::shared_ptr<bt::itransition_target> photo;

        // find the target photo
        if ( (_intermediateState == intermediate_state::photoRecreated) && 
             (_intermediateRecreatedPhoto->storageId() == photoId) )
        {
            photo = std::dynamic_pointer_cast<bt::itransition_target>(_intermediateRecreatedPhoto);
        }
        else
        {
            // it is a page, find the specific page...
            for (auto &iter : _photos)
            {
                if (iter->storageId() == photoId)
                {
                    photo = std::dynamic_pointer_cast<bt::itransition_target>(iter);
                    break;
                }
            }
        }

        // propagate the redo
        if (photo != nullptr)
        {
            photo->redo(path, childIndex, transitionParameters);
        }
    }
}
